home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / admin / makedev-.5 / makedev- / makedev-1.5 / devices.c next >
C/C++ Source or Header  |  1995-03-25  |  21KB  |  712 lines

  1. /*
  2.  * devices.c: Code for MAKEDEV, a program to create entries in /dev.
  3.  *
  4.  * Based on the MAKEDEV shell script, version 2.0, distributed with
  5.  * util-linux 1.10 and written by Nick Holloway. 
  6.  *
  7.  * A number of bugs were fixed, and some additional features added.
  8.  * Written 10-Dec-94 by David A. Holland, dholland@husc.harvard.edu
  9.  * Rik Faith (faith@cs.unc.edu) contributed ideas and patches.
  10.  *
  11.  * Copyright 1994, 1995. All rights reserved. 
  12.  * See the file LEGAL.NOTICE for conditions of redistribution.
  13.  *
  14.  * Bugs:
  15.  *    None known right now.
  16.  *
  17.  * History:
  18.  *
  19.  * Version 2: 25-Mar-95    Fixed makefile. 
  20.  *    Look for config files in ".." under testing conditions.
  21.  *    Big source split: makedev.syn -> parser.syn and devices.c.
  22.  *    Consequently, this file's version numbers aren't the same as the
  23.  *    whole program's any more.
  24.  * Version 1.4b: 25-Mar-95 Merged Rik's changes. Additional bug fixes:
  25.  *    Don't leave off the last entry in a range.
  26.  *    Parse hex digits correctly [sigh...].
  27.  *    Now we actually *use* the ishex flag.
  28.  * Version 1.4a: 26-Feb-95 Forced devinfo and makedev.cfg to be in /etc.
  29.  *                         [from faith@cs.unc.edu]
  30.  * Version 1.4: 15-Jan-95  Wrote man pages. Now reads DEVINFO.local.
  31.  * Version 1.3: 31-Dec-94  Bug fixes. Added batches. Added omits.
  32.  * Version 1.2: 11-Dec-94  Add configuration file parsing.
  33.  * Version 1.1: 11-Dec-94  Distinguish block and character devices in the
  34.  *    table of major device numbers. Changed the name and format of the
  35.  *    update cache file to include the type. It appears that the old script
  36.  *    was broken in this regard.
  37.  * Version 1.0: 10-Dec-94  Initial version.
  38.  */
  39.  
  40. #include <stdio.h>
  41. #include <stdlib.h>
  42. #include <string.h>
  43. #include <stdarg.h>
  44. #include <unistd.h>
  45. #include <fcntl.h>
  46. #include <pwd.h>
  47. #include <grp.h>
  48. #include <sys/stat.h>
  49.  
  50. #include "devices.h"
  51. #include "version.h"
  52.  
  53. static int isverbose=NO;  /* flag: print out what we do? */
  54. static int deletion=NO;   /* flag: delete instead of create */
  55. static int donothing=NO;  /* flag: don't actually do anything */
  56.  
  57. /*
  58.  * Roll over and die.
  59.  */
  60. void crash(const char *msg) {
  61.   fprintf(stderr, "MAKEDEV: %s\n", msg);
  62.   exit(1);
  63. }
  64.  
  65. /*
  66.  * Print a warning.
  67.  */
  68. void warn(const char *format, ...) {
  69.   va_list ap;
  70.   va_start(ap, format);
  71.   fprintf(stderr, "MAKEDEV: ");
  72.   vfprintf(stderr, format, ap);
  73.   fprintf(stderr, "\n");
  74.   va_end(ap);
  75. }
  76.  
  77. /*
  78.  * Translate string name to uid.
  79.  */
  80. static uid_t name2uid(const char *name) {
  81.   struct passwd *p = getpwnam(name);
  82.   if (!p) warn("undefined user: %s, using uid 0", name);
  83.   return p ? p->pw_uid : 0;  /* make things owned by root by default */
  84. }
  85.  
  86. /*
  87.  * Translate string name to gid.
  88.  */
  89. static gid_t name2gid(const char *name) {
  90.   struct group *g = getgrnam(name);
  91.   if (!g) warn("undefined group: %s, using gid 0", name);
  92.   return g ? g->gr_gid : 0;  /* group 0 is a good default too */
  93. }
  94.  
  95. /************************* device classes *************************/
  96.  
  97. /*
  98.  * A device class is a string attached to the device which tells us
  99.  * what set of permissions and ownership should be used. This is the
  100.  * table of classes.
  101.  */
  102.  
  103. typedef struct {
  104.     const char *classname;
  105.     const char *owner;
  106.     const char *group;
  107.     int mode;
  108. } devclass;
  109.  
  110. #define MAXCLASSES 32
  111. static devclass classes[MAXCLASSES];
  112. static int nclasses=0;
  113.  
  114. void addclass(const char *name, const char *o, const char *g, int m) {
  115.   if (nclasses>=MAXCLASSES) crash("out of space for device classes");
  116.   classes[nclasses].classname = name;
  117.   classes[nclasses].owner = o;
  118.   classes[nclasses].group = g;
  119.   classes[nclasses].mode = m;
  120.   nclasses++;
  121.   name2uid(o);  /* check for undefined users/groups */
  122.   name2gid(g);
  123. }
  124.  
  125. static void loadclasses(void) {
  126.   FILE *f;
  127. #ifdef TESTING
  128.   f = fopen("../makedev.cfg", "r");
  129. #else
  130.   f = fopen("/etc/makedev.cfg", "r");
  131. #endif
  132.   if (!f) crash("Can't load /etc/makedev.cfg");
  133.   doparse(f, 4, "makedev.cfg");
  134.   fclose(f);
  135. }
  136.  
  137. /*
  138.  * Return the index into the above table for a particular class name.
  139.  */
  140. static int which_class(const char *name) {
  141.   int i;
  142.   for (i=0; i<nclasses; i++)
  143.     if (!strcmp(classes[i].classname, name)) return i;
  144.   return 0;
  145. }
  146.  
  147. /*
  148.  * Produce an "ls -l"-ish mode string.
  149.  */
  150. static const char *modestring(int mode) {
  151.   static char rv[12];
  152.   int i,z;
  153.   strcpy(rv, "rwxrwxrwx");
  154.   for (i=8,z=1; i>=0; i--, z<<=1) if (!(mode&z)) rv[i]='-';
  155.   return rv;
  156. }
  157.  
  158. /*
  159.  * Create (or delete, or update) a block or character device.
  160.  */
  161. static void class_makedev(const char *name, const char *class,
  162.               int major, int minor, char type) {
  163.   int x = which_class(class), mode = classes[x].mode;
  164.   const char *owner = classes[x].owner, *group = classes[x].group;
  165.   if (isverbose) {
  166.     if (deletion) printf("rm -f %s\n", name);
  167.     else printf("%c%s   1 %-8s %-8s %3d, %3d for %s\n", type,
  168.         modestring(mode), owner, group, major, minor, name);
  169.   }
  170.   if (donothing) return;
  171.   if (unlink(name) && deletion) warn("Couldn't remove %s\n", name);
  172.   if (!deletion) {
  173.     dev_t q = (major<<8) | minor;
  174.     if (mknod(name, type=='c' ? S_IFCHR : S_IFBLK,  q) ||
  175.     chown(name, name2uid(owner), name2gid(group)) ||
  176.     chmod(name, mode)) {
  177.       warn("couldn't create %s: %s", name, strerror(errno));
  178.     }
  179.   }
  180. }
  181.  
  182. /************************* major number list *************************/
  183.  
  184. /*
  185.  * In Linux device major numbers can be allocated dynamically, so we go
  186.  * look in /proc/devices to see what they are. This keeps track of things.
  187.  */
  188.  
  189. typedef struct {
  190.     const char *procname;
  191.     int flag;
  192. } majorentry;
  193.  
  194. #define MAXMAJORS 256
  195. static majorentry cmajors[MAXMAJORS];  /* initialized to 0 */
  196. static majorentry bmajors[MAXMAJORS];  /* initialized to 0 */
  197. static int no_proc=0;   /* true if we didn't find /proc/devices */
  198.  
  199. /*
  200.  * Store the name associated with a particular major device number.
  201.  */
  202. void set_major(const char *procname, int ischar, int num) {
  203.   if (num<0 || num>255) {
  204.     warn("warning: got bogus major number %d for %s", num, procname);
  205.     return;
  206.   }
  207.   if (ischar) cmajors[num].procname=procname;
  208.   else bmajors[num].procname=procname;
  209. }
  210.  
  211. /*
  212.  * Look up a major device number by name; return the default value
  213.  * if provided. A default value of -1 implies the device is only
  214.  * dynamic, and so if there's no entry we shouldn't even note its
  215.  * existence.
  216.  */
  217. int get_major(const char *procname, int ischar, int defaalt) {
  218.   int i;
  219.   if (!procname) return defaalt;
  220.   if (ischar) {
  221.     for (i=0; i<MAXMAJORS; i++)
  222.       if (cmajors[i].procname && !strcmp(cmajors[i].procname, procname))
  223.     return i;
  224.   }
  225.   else {
  226.     for (i=0; i<MAXMAJORS; i++)
  227.       if (bmajors[i].procname && !strcmp(bmajors[i].procname, procname))
  228.     return i;
  229.   }
  230.   return defaalt;
  231. }
  232.  
  233. /*
  234.  * Read /proc/devices.
  235.  */
  236. static void setup_majors(void) {
  237.   FILE *f = fopen("/proc/devices", "r");
  238.   if (!f) {
  239.     fprintf(stderr, "MAKEDEV: warning: can't read /proc/devices\n");
  240.     no_proc = 1;
  241.     return;
  242.   }
  243.   doparse(f, 1, "/proc/devices");
  244.   fclose(f);
  245. }
  246.  
  247. /************************** procname list *************************/
  248.  
  249. /*
  250.  * The names found in /proc/devices aren't usually quite the same
  251.  * as the names we use. This is a mapping between the two namespaces.
  252.  */
  253. typedef struct {
  254.     const char *procname;
  255.     const char *groupname;
  256. } namealias;
  257.  
  258. #define MAXALIASES 100
  259. static namealias aliases[MAXALIASES];
  260. static int naliases=0;
  261.  
  262. void addalias(const char *procname, const char *groupname) {
  263.   if (naliases>=MAXALIASES) crash("out of space for aliases");
  264.   aliases[naliases].procname = procname;
  265.   aliases[naliases].groupname = groupname;
  266.   naliases++;
  267. }
  268.  
  269. void ignore_procname(const char *procname) {
  270.   addalias(procname, NULL);
  271. }
  272.  
  273. static const char *procnameof(const char *groupname) {
  274.   int i;
  275.   for (i=0; i<naliases; i++) if (!strcmp(groupname, aliases[i].groupname))
  276.     return aliases[i].procname;
  277.   return NULL;
  278. }
  279.  
  280. static const char *groupnameof(const char *procname) {
  281.   int i;
  282.   for (i=0; i<naliases; i++) if (!strcmp(procname, aliases[i].procname))
  283.     return aliases[i].groupname;
  284.   return NULL;
  285. }
  286.  
  287. /************************* batch list *************************/
  288. /*
  289.  * Create a device "batch" - a bunch of devices or groups.
  290.  * This is used for "generic" and automatically for disk entries.
  291.  * (Disk entries for "hd" come up with groups hda, hdb, etc., but
  292.  * "hd" itself needs to run these too.)
  293.  */
  294. #define MAXBATCHES 16
  295.  
  296. /* batch structure is in devices.h */
  297.  
  298. static batch batches[MAXBATCHES];
  299. static int nbatches=0;
  300.  
  301. /*
  302.  * Start a new batch.
  303.  */
  304. batch *addbatch(const char *name) {
  305.   batch *b;
  306.   if (nbatches>=MAXBATCHES) crash("Out of space for batches");
  307.   b = &batches[nbatches++];
  308.   b->name = name;
  309.   b->busy = NO;
  310.   return b;
  311. }
  312.  
  313. /*
  314.  * Add something to a batch.
  315.  */
  316. batch *add2batch(batch *b, const char *target) {
  317.   if (b->ntargets>=MAXTARGETS) {
  318.     warn("Too many targets for batch %s (max %d)", b->name, MAXTARGETS);
  319.     return b;
  320.   }
  321.   b->targets[b->ntargets++] = target;
  322.   return b;
  323. }
  324.  
  325. /*
  326.  * Run a batch.
  327.  */
  328. static void run_batch(const batch *b, makeopts m) {
  329.   int i;
  330.   for (i=0; i<b->ntargets; i++) make(b->targets[i], m);
  331. }
  332.  
  333. /*
  334.  * Try to run a batch; returns YES if it found one.
  335.  */
  336. static int try_run_batch(const char *name, makeopts m) {
  337.   int i;
  338.   for (i=0; i<nbatches; i++) {
  339.     if (!strcmp(name, batches[i].name)) {
  340.       if (batches[i].busy) {
  341.     warn("Found recursive batch definition for %s", batches[i].name);
  342.     continue;
  343.       }
  344.       batches[i].busy=YES;
  345.       run_batch(&batches[i], m);
  346.       batches[i].busy=NO;
  347.       return YES;
  348.     }
  349.   }
  350.   return NO;
  351. }
  352.  
  353. /************************* device list *************************/
  354.  
  355. /*
  356.  * Structure to remember the properties of an individual device.
  357.  * NOTE: if the device is actually a symbolic link, the "class"
  358.  * member is used to store the thing it should be linked to.
  359.  */
  360. typedef struct {
  361.     const char *name;   /* file name to create */
  362.     const char *grp;    /* device "group" name (e.g. "busmice") */
  363.     const char *class;  /* device class ( -> owner and permissions) */
  364.     int major, minor;   /* device number */
  365.     char type;          /* 'c', 'b', or 'l' for symbolic link */
  366.     int omit;           /* don't make me if this is nonzero */
  367. } device;
  368.  
  369. /*
  370.  * Create a device (link or actual "special file") - special files are
  371.  * passed on to class_makedev().
  372.  */
  373. void makedev(device *d, makeopts m) {
  374.   if (m==M_OMIT) {
  375.     d->omit=1;
  376.   }
  377.   if (d->omit==1) return;
  378.   if (d->type=='l') {
  379.     if (isverbose) {
  380.       if (deletion) printf("rm -f %s\n", d->name);
  381.       else printf("lrwxrwxrwx   %s -> %s\n", d->name, d->class);
  382.     }
  383.     if (donothing) return;
  384.     if (unlink(d->name) && deletion) warn("Couldn't remove %s\n", d->name);
  385.     if (!deletion) {
  386.       if (symlink(d->class, d->name)) /* class holds thing pointed to */
  387.     warn("couldn't link %s -> %s: %s", d->name, d->class, strerror(errno));
  388.     }
  389.   }
  390.   else class_makedev(d->name, d->class, d->major, d->minor, d->type);
  391. }
  392.  
  393. /*
  394.  * Array of devices. We allocate it once from main(); it doesn't grow.
  395.  * Should maybe make it growable sometime. This keeps track of all possible
  396.  * devices. We build this thing first, and then create devices from it as
  397.  * requested.
  398.  */
  399. static device *devices = NULL;
  400. static int maxdevices, ndevices;
  401.  
  402. /*
  403.  * Allocate space for the device array.
  404.  */
  405. static void allocate_devs(int nd) {
  406.   devices = malloc(nd * sizeof(device));
  407.   if (!devices) crash("Out of memory");
  408.   ndevices = 0;
  409.   maxdevices = nd;
  410. }
  411.  
  412. /*
  413.  * Check all the devices for having valid device classes.
  414.  */
  415. static void check_classes(void) {
  416.   int i;
  417.   const char *q=NULL;
  418.   for (i=0; i<ndevices; i++) 
  419.     if (devices[i].type!='l' && !devices[i].omit &&
  420.     which_class(devices[i].class)<0) {
  421.       if (!q || strcmp(q, devices[i].class)) {
  422.     warn("Invalid device class %s for %s", 
  423.          devices[i].class, devices[i].name);
  424.     q = devices[i].class;
  425.       }
  426.       devices[i].class = "default";
  427.     }
  428. }
  429.  
  430. /*
  431.  * Create an entry in the device table for a single device.
  432.  */
  433. void init(const char *name, const char *grp, const char *class,
  434.          int major, int minor, int type) {
  435.   if (major < 0) return;
  436.   if (!strchr("bcl", type)) {
  437.     warn("invalid device type %c for %s (skipping)", type, name);
  438.     return;
  439.   }
  440.   if (ndevices>=maxdevices) crash("out of space for devices");
  441.   devices[ndevices].name = name;
  442.   devices[ndevices].grp = grp;
  443.   devices[ndevices].class = class;
  444.   devices[ndevices].major = major;
  445.   devices[ndevices].minor = minor;
  446.   devices[ndevices].type = type;
  447.   devices[ndevices].omit = 0;
  448.   ndevices++;
  449. }
  450.  
  451. /*
  452.  * Create an entry for a symbolic link "device", such as /dev/fd
  453.  * (which is a symbolic link to /proc/self/fd)
  454.  */
  455. void initlink(const char *name, const char *grp, const char *target) {
  456.   init(name, grp, target, 0, 0, 'l');
  457. }
  458.  
  459. /*
  460.  * Init lots of devices. This creates a number of devices, numbered between
  461.  * lo and hi. The idea is that "base" contains a %d or %x (or something like
  462.  * that) in it which pulls in the number. The device group can also do this,
  463.  * though this will in most cases not be useful. "baseminor" is the minor
  464.  * number of the first device created.
  465.  */
  466. void initlots(const char *base, int lo, int hi, const char *grp,
  467.              const char *class,
  468.              int maj, int baseminor, int type) {
  469.   char buf[32], gbuf[32];
  470.   int i;
  471.   if (maj<0) return;
  472.   for (i=lo; i<=hi; i++) {
  473.     sprintf(buf, base, i);
  474.     if (grp) sprintf(gbuf, grp, i);  /* grp is permitted to contain a %d */
  475.     init(strdup(buf), grp ? strdup(gbuf) : NULL, class, 
  476.      maj, baseminor+i-lo, type);
  477.   }
  478. }
  479.  
  480. /*
  481.  * Init a whole (hard) disk's worth of devices - given `hd', it makes
  482.  * hda1...hda8 through hdd1...hdd8 in one fell swoop. "low" and "high"
  483.  * are the letters to use ('a' and 'd' for the previous example).
  484.  * "nparts" is the number of partitions to create, ordinarily 8.
  485.  * "maj" is the major device number; minmult is the multiplier for the
  486.  * minor number. That is, if hda starts at 0, and hdb starts at 64, minmult
  487.  * is 64.
  488.  *
  489.  * Note that it creates "hda", "hdb", etc. too, and puts things in the
  490.  * groups "hda", "hdb", etc. as appropriate. The class is set to "disk".
  491.  */
  492. void initdisk(const char *base, int low, int high, int nparts,
  493.           int maj, int minmult) {
  494.   char buf[16], buf2[16];
  495.   int i;
  496.   batch *b;
  497.   if (maj<0) return;
  498.   if (low>=high) return;
  499.   b = addbatch(base);
  500.   for (i=low; i<=high; i++) {
  501.     char *q;
  502.     sprintf(buf, "%s%c", base, i);
  503.     q = strdup(buf);
  504.     init(q, q, "disk", maj, (i-low)*minmult,   'b');
  505.     strcpy(buf2, buf);
  506.     strcat(buf2, "%d");
  507.     initlots(buf2, 1, nparts, buf, "disk", maj, (i-low)*minmult+1, 'b');
  508.     add2batch(b, q);
  509.   }
  510. }
  511.  
  512. static void initdevs(void) {
  513.   FILE *f;
  514. #ifdef TESTING
  515.   f = fopen("../devinfo", "r");
  516. #else
  517.   f = fopen("/etc/devinfo", "r");
  518. #endif
  519.   if (!f) crash("Can't read /etc/devinfo");
  520.   doparse(f,3, "devinfo");
  521.   fclose(f);
  522.   f = fopen("/usr/local/etc/devinfo.local", "r");
  523.   if (!f) f = fopen("/etc/devinfo.local", "r");
  524.   if (f) {
  525.     doparse(f,3, "devinfo.local");
  526.     fclose(f);
  527.   }
  528. }
  529.  
  530. /************************** update *************************/
  531.  
  532. /*
  533.  * Call make() with our names for something that appeared in /proc/devices.
  534.  */
  535.  
  536. static void transmake(const char *procname, makeopts m) {
  537.   const char *gname = groupnameof(procname);
  538.   if (gname) make(gname, m);
  539. }
  540.  
  541. /*
  542.  * Update a device that appeared in MAKEDEV.cache. Whenever we update,
  543.  * we save what we did into MAKEDEV.cache; this lets us avoid doing
  544.  * them over the next time. We only do something if the device has
  545.  * disappeared or the major number has changed.
  546.  *
  547.  * Note that this caching made the shell version go much faster (it took
  548.  * around 15 seconds with the cache, vs. over a minute if the cache was
  549.  * blown away.) For us, it still does, but it hardly matters: it shaves
  550.  * one second off a two-second execution.
  551.  *
  552.  * Also note the old script used DEVICES instead of MAKEDEV.cache. We
  553.  * changed because the old file didn't record whether something was
  554.  * a block or character device; since the sets of numbers are independent,
  555.  * this was bound to break.
  556.  */
  557. static void update2(const char *name, int ischar, int major) {
  558.   int now = get_major(name, ischar, -1);
  559.   if (now<0) {
  560.     deletion = 1;   /* must have been zero if we're doing an update */
  561.     transmake(name, M_CREATE);
  562.     deletion = 0;
  563.   }
  564.   else if (now!=major) { /* oops, it moved; remake it */
  565.     transmake(name, M_CREATE);
  566.     if (ischar) cmajors[now].flag=1;
  567.     else bmajors[now].flag=1;
  568.   }
  569.   else {
  570.     if (ischar) cmajors[now].flag=1; /* unchanged; inhibit remaking it */
  571.     else bmajors[now].flag=1; /* unchanged; inhibit remaking it */
  572.   }
  573. }
  574.  
  575. void updatefromcache(const char *name, int major, int type) {
  576.   update2(name, type=='c', major);
  577. }
  578.  
  579.  
  580. /*
  581.  * Update. Read the information stored in MAKEDEV.cache from the last
  582.  * update; fix anything that changed; then create any new devices that
  583.  * weren't listed the last time. (We use the "flag" field in the
  584.  * majors array to check this.) At that point, write out a new
  585.  * cache file.
  586.  */
  587. #define CACHEFILE "MAKEDEV.cache"
  588.  
  589. static void update(void) {
  590.   FILE *f;
  591.   int i;
  592.   if (no_proc) { warn("Couldn't read anything from /proc/devices"); return; }
  593.   if (deletion) { warn("update and -d are incompatible"); return; }
  594.   f = fopen(CACHEFILE, "r");
  595.   if (f) {
  596.     doparse(f, 2, CACHEFILE);
  597.     fclose(f);
  598.   }
  599.   for (i=0; i<MAXMAJORS; i++) {
  600.     if (cmajors[i].procname && !cmajors[i].flag) {
  601.       transmake(cmajors[i].procname, M_CREATE);
  602.       cmajors[i].flag=1;
  603.     }
  604.     if (bmajors[i].procname && !bmajors[i].flag) {
  605.       transmake(bmajors[i].procname, M_CREATE);
  606.       bmajors[i].flag=1;
  607.     }
  608.   }
  609.   if (donothing) return;
  610.   f = fopen(CACHEFILE, "w");
  611.   if (f) {
  612.     for (i=0; i<MAXMAJORS; i++)  {
  613.       if (cmajors[i].procname) fprintf(f, "%s %d char\n", cmajors[i].procname, i);
  614.       if (bmajors[i].procname) fprintf(f, "%s %d block\n", bmajors[i].procname, i);
  615.     }
  616.     fclose(f);
  617.   }
  618.   else warn("warning: can't write MAKEDEV.cache");
  619. }
  620.  
  621. /************************* work *************************/
  622.  
  623. /*
  624.  * Create (or delete, etc. according to flags) a device or device group.
  625.  * The "generic" group is handled specially by recursing once.
  626.  * "update" is handled specially; see update() below.
  627.  * "local" issues a warning; people should use DEVINFO.local instead.
  628.  */
  629. void make(const char *what, makeopts m) {
  630.   int i;
  631.   if (!strcmp(what, "update")) {
  632.     if (m!=M_CREATE) warn("update not compatible with those options");
  633.     else update();
  634.   }
  635.   else if (!strcmp(what, "local")) {
  636.     warn("The local target is obsolete.");
  637.   }
  638.   else if (!try_run_batch(what, m)) {
  639.     int found=0;
  640.     for (i=0; i<ndevices; i++) {
  641.       if ((devices[i].grp && !strcmp(what, devices[i].grp)) ||
  642.           !strcmp(what, devices[i].name)) {
  643.         makedev(&devices[i], m);
  644.         found = 1;
  645.       }
  646.     }
  647.     if (!found) warn("unknown device or device group %s", what);
  648.   }
  649. }
  650.  
  651. /*
  652.  * A major improvement over the shell version...
  653.  */
  654. static void usage(void) {
  655.   printf("%s usage:\n", MYNAME);
  656.   printf("    %s [-vdcn] device [device...]\n", MYNAME);
  657.   printf("      -v          Verbose output\n");
  658.   printf("      -d          Remove specified devices\n");
  659.   printf("      -c          Create devices (default)\n");
  660.   printf("      -n          Don't actually do anything (implies -v)\n");
  661.   printf("      -I          Immediate; work in current directory, not /dev\n");
  662.   printf("      -V          Print version information\n");
  663.   printf("\n");
  664. }
  665.  
  666. static void setcwd(void) {
  667.   char buf[64];
  668.   if (!getcwd(buf, sizeof(buf)) || strcmp(buf, "/dev")) {
  669.       printf("Notice: changing my directory to /dev.\n");
  670.       chdir("/dev");
  671.   }
  672. }
  673.  
  674. /*
  675.  * We should use getopt one of these days.
  676.  */
  677. int main(int argc, char **argv) {
  678.   int i,j, done=0;
  679.   int immediate;
  680. #ifdef TESTING
  681.   immediate=1;   /* you don't think I run buggy versions in /dev, do you? */
  682. #else
  683.   immediate=0;
  684. #endif
  685.  
  686.   for (i=1; i<argc && argv[i][0]=='-' && !done; i++) {
  687.     for (j=1; argv[i][j] && !done; j++) switch(argv[i][j]) {
  688.         case '-': done=1; break;
  689.     case 'v': isverbose = 1; break;
  690.     case 'd': deletion = 1; break;
  691.     case 'c': deletion = 0; break;
  692.     case 'n': donothing = 1; isverbose = 1; break;
  693.     case 'h': usage(); exit(0);
  694.         case 'I': immediate = 1; break;
  695.     case 'V': printf("MAKEDEV: %s\n", version); exit(0);
  696.     default: fprintf(stderr, "%s: unknown flag %c\n", MYNAME, argv[i][j]);
  697.       exit(1);
  698.     }
  699.   }
  700.   if (!immediate) setcwd();  /* go into /dev */
  701.   setup_majors();      /* read major device numbers from /proc */
  702.   allocate_devs(1500); /* make space to hold devices */
  703.   initdevs();          /* set up device structures */
  704.   loadclasses();       /* load device classes from config file */
  705.   check_classes();     /* make sure no devices have bogus classes */
  706.   if (i==argc) warn("didn't do anything; try -h for help.");
  707.   else for (; i<argc; i++) make(argv[i], M_CREATE);
  708.   return 0;
  709. }
  710.  
  711.  
  712.